import { Asset } from 'cc';
import { SoundManager } from '../mgr/sound/SoundManager';
import { UIManager } from '../mgr/ui/UIManager';
import { AppGlobalUtility, AppModuleUtility } from '../utillity/Utillity';
import { app } from '../XForge';
import { BaseController, BaseControllerType } from './BaseController';
import { BaseModel, BaseModelType } from './BaseModel';

interface Action<T = void> {
    (result: T): void;
}

const moduleTypeMap = new Map<string, any>();
export function getModuleType(moduleName: string): { new(): BaseModule } {
    return moduleTypeMap.get(moduleName);
}

export function getGlobalType(): { new(): BaseModule } {
    return moduleTypeMap.get(AppGlobalUtility.getModuleTypeName());
}

/**
 * 模块装饰器
 */
export function module(moduleName: string) {
    return function fNOP(ctor: any) {
        ctor._moduleName = moduleName;
        moduleTypeMap.set(AppModuleUtility.getModuleTypeName(moduleName), ctor);
        return ctor;
    };
}
/**
 * 全局模块装饰器
 */
export function global() {
    return function fNOP(ctor: any) {
        ctor._moduleName = 'Global';
        moduleTypeMap.set(AppGlobalUtility.getModuleTypeName(), ctor);
        return ctor;
    };
}

export abstract class CoreModule {
    private static _moduleName = '';
    /**
     * 获取module名字
     */
    public static get moduleName() {
        return this._moduleName;
    }
    /**
     * 获取module名字
     */
    public get moduleName() {
        return (this.constructor as typeof CoreModule)._moduleName;
    }

    /**
     * 加载AssetBundle目录下的资源
     */
    public abstract loadAsset<T extends typeof Asset>(path: string, type: T, onCompleted: Action<InstanceType<T> | null>, onProgress?: Action<number>): void;

    // ### Model ###
    private readonly modelCacheMap = new Map<BaseModelType, BaseModel>();

    public useModel<T extends BaseModelType>(type: T): InstanceType<T> {
        if (this.modelCacheMap.has(type)) {
            return this.modelCacheMap.get(type) as InstanceType<T>;
        }

        const model = new type() as InstanceType<T>;
        BaseModel.Init(model, this);

        this.modelCacheMap.set(type, model);
        return model;
    }
    public removeModel(type: BaseModelType): void {
        if (this.modelCacheMap.has(type)) {
            this.modelCacheMap.delete(type);
        }
    }
    // ###END###

    // ### Manager ###
    private _ui: UIManager;
    public get ui() {
        if (this._ui) {
            return this._ui;
        }
        this._ui = new UIManager();
        UIManager.init(this._ui, this);
        return this._ui;
    }

    private _sound: SoundManager;
    public get sound() {
        if (this._sound) {
            return this._sound;
        }
        this._sound = new SoundManager();
        SoundManager.init(this._sound, this);
        return this._sound;
    }
    // ###END###

    /**
     * 初始化
     * @param onCompleted 加载完成回调函数
     * @param onProgress 加载进度回调函数
     */
    protected init(onCompleted: () => void, onProgress: (result: number) => void): void {
        onProgress;
        onCompleted();
    }

    /**
     * 加载完成后
     */
    protected onLoad() {

    }

    /**
     * 卸载完成后
     */
    protected onUnload() {

    }
}

export abstract class BaseModule extends CoreModule {
    public static load(module: BaseModule, onCompleted: Action = null, onProgress: Action<number> = null) {
        app.lib.task.createSync()
            .add(next => {
                app.lib.loader.loadBundle({
                    bundle: AppModuleUtility.getAssetBundleName(module.moduleName),
                    onComplete: next
                });
            })
            .add(next => {
                module.init(next, (progress) => {
                    onProgress?.(progress);
                });
            })
            .start(() => {
                module.onLoad();
                onCompleted?.();
            });
    }

    public static unload(module: BaseModule) {
        module.onUnload();
        // 释放UIManager中的缓存
        module.ui.release();
        // 释放SoundManager中的缓存
        module.sound.release();
        app.lib.loader.unloadBundle(AppModuleUtility.getAssetBundleName(module.moduleName));
    }

    public loadAsset<T extends typeof Asset>(path: string, type: T, onCompleted: Action<InstanceType<T> | null>, onProgress?: Action<number>): void {
        app.lib.loader.loadAsset({
            path: path,
            type: type,
            bundle: AppModuleUtility.getAssetBundleName(this.moduleName),
            onComplete: onCompleted,
            onProgress: onProgress,
        });
    }

    // ### Controller ###
    private readonly controllerCacheMap = new Map<BaseControllerType, BaseController>();

    public useController<T extends BaseControllerType>(type: T): InstanceType<T> {
        if (this.controllerCacheMap.has(type)) {
            return this.controllerCacheMap.get(type) as InstanceType<T>;
        }

        const controller = new type() as InstanceType<T>;
        BaseController.Init(controller, this);

        this.controllerCacheMap.set(type, controller);
        return controller;
    }
    public removeController(type: BaseControllerType): void {
        if (this.controllerCacheMap.has(type)) {
            this.controllerCacheMap.delete(type);
        }
    }
    // ###END###
}

export abstract class BaseGlobal extends CoreModule {

    public static load(module: BaseGlobal, onCompleted: Action<BaseGlobal> = null, onProgress: Action<number> = null) {
        app.lib.task.createSync()
            .add(next => {
                app.lib.loader.loadBundle({
                    bundle: AppGlobalUtility.getAssetBundleName(),
                    onComplete: next
                });
            })
            .add(next => {
                module.init(next, (progress) => {
                    onProgress?.(progress);
                });
            })
            .start(() => {
                module.onLoad();
                onCompleted?.(module);
            });
    }

    public static unload(module: BaseGlobal) {
        module.onUnload();
        // 释放UIManager中的缓存
        module.ui.release();
        // 释放SoundManager中的缓存
        module.sound.release();
        app.lib.loader.unloadBundle(AppGlobalUtility.getAssetBundleName());
    }

    public loadAsset<T extends typeof Asset>(path: string, type: T, onCompleted: Action<InstanceType<T> | null>, onProgress?: Action<number>): void {
        app.lib.loader.loadAsset({
            path: path,
            type: type,
            bundle: AppGlobalUtility.getAssetBundleName(),
            onComplete: onCompleted,
            onProgress: onProgress,
        });
    }
}